﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using tinfosvr.Models;

namespace tinfosvr
{
    class TrainTimesUpdater
    { 
        private Dictionary<int, qy_city> mCities = new Dictionary<int, qy_city>();
        private Dictionary<int, qy_line> mLines = new Dictionary<int, qy_line>();
        private Dictionary<int, qy_station> mStations = new Dictionary<int, qy_station>();
        private List<train_schedule_item> mTrainSchedules = new List<train_schedule_item>();
        private List<qy_platform> mPlatforms = new List<qy_platform>();
        private Timer mTmrCheck = new Timer();
        private readonly int mTrainArriveDiff = -15;
        private static TrainTimesUpdater instance = new TrainTimesUpdater();
        private TrainTimesUpdater()
        {
            mTmrCheck.Interval = 10 * 1000;
            mTmrCheck.Elapsed += MTmrCheck_Elapsed;
        }

        public static TrainTimesUpdater GetInstance()
        {
            return instance;
        }

        public bool LoadData()
        {
            try
            {
                LoadCity();
                LoadLine();
                LoadStation();
                LoadPlatform();
                LoadTrainSchedule();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public void StartUpdate()
        {
            mTmrCheck.Start();
        }

        public void StopUpdate()
        {
            mTmrCheck.Stop();
        }

        public List<qy_platform> GetPlatform(int citycode, int stationid)
        {
            List<qy_platform> platforms = mPlatforms.Where(x => x.citycode == citycode && x.station_id == stationid).ToList();
            return platforms;
        }

        public bool UpdateTrainTimesInfo(qy_platform info)
        {
            //查找本站台的列车时刻表
            int weekDay = (int)DateTime.Now.DayOfWeek;
            if(weekDay == 0) // sunday==0
            {
                weekDay = 7;
            }

            var trainSchedules = mTrainSchedules.Where(train =>
            train.trainSchedule.week[weekDay - 1] == '1'
            && train.trainSchedule.platform_id == info.id).ToList().OrderBy(x => x.arriveTimeSecondsInDay).ToList();

            if (trainSchedules == null || trainSchedules.Count == 0)
            {
                return false;
            }

            //根据时刻表更新stationinfo的信息
            //info.points = "";
            string firstTrain = trainSchedules.First().trainSchedule.arrive_time;
            string lastTrain = trainSchedules.Last().trainSchedule.arrive_time;
            string origStation = "";
            string stopStation = "";
            int curTrainTime = -1;
            int nextTrainTime = -1;
            int idx = GetClosestIndexByLess(trainSchedules, (int)DateTime.Now.TimeOfDay.TotalSeconds);

            if (idx < 0)
            {
                curTrainTime = trainSchedules[0].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                int sid = trainSchedules[0].trainSchedule.origstation_id;
                if(mStations.ContainsKey(sid))
                {
                    origStation = mStations[sid].lb;
                }

                sid = trainSchedules[0].trainSchedule.terminus_id;
                if(mStations.ContainsKey(sid))
                {
                    stopStation = mStations[sid].lb;
                }

                if (trainSchedules.Count > 1)
                {
                    nextTrainTime = trainSchedules[1].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                }
            }
            else if (idx == trainSchedules.Count - 1)
            {
                int arriveDiff = trainSchedules[idx].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                if (arriveDiff >= mTrainArriveDiff)
                {
                    curTrainTime = 0;//
                }

                int sid = trainSchedules[idx].trainSchedule.origstation_id;
                if (mStations.ContainsKey(sid))
                {
                    origStation = mStations[sid].lb;
                }

                sid = trainSchedules[idx].trainSchedule.terminus_id;
                if (mStations.ContainsKey(sid))
                {
                    stopStation = mStations[sid].lb;
                }
            }
            else
            {
                int arriveDiff = trainSchedules[idx].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                if (arriveDiff >= mTrainArriveDiff)
                {
                    curTrainTime = 0;
                }
                else
                {
                    curTrainTime = trainSchedules[idx + 1].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                    if (idx + 2 <= trainSchedules.Count - 1)
                    {
                        nextTrainTime = trainSchedules[idx + 2].arriveTimeSecondsInDay - (int)DateTime.Now.TimeOfDay.TotalSeconds;
                    }
                }

                int sid = trainSchedules[idx].trainSchedule.origstation_id;
                if (mStations.ContainsKey(sid))
                {
                    origStation = mStations[sid].lb;
                }

                sid = trainSchedules[idx].trainSchedule.terminus_id;
                if (mStations.ContainsKey(sid))
                {
                    stopStation = mStations[sid].lb;
                }
            }

            info.direction = stopStation;
            info.first_time = firstTrain;
            info.last_time = lastTrain;
            info.points = origStation + "-" + stopStation;
            info.current_train = curTrainTime;
            info.next_train = nextTrainTime;
            return true;
        }

        private void DoUpdateTrainTimes()
        {
            try
            {
                using (var context = new mdfqyContext())
                {
                    List<qy_platform> platforms = new List<qy_platform>();
                    var platform = context.qy_platform.ToList();
                    foreach (qy_platform info in platform)
                    {
                        if (UpdateTrainTimesInfo(info))
                        {
                            platforms.Add(info);
                        }
                    }

                    if (platforms.Count > 0)
                    {
                        context.qy_platform.UpdateRange(platforms);
                    }
                }
            }
            catch(Exception ex)
            {
            }
        }

        private void MTmrCheck_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                mTmrCheck.Stop();
            }
            catch(Exception ex)
            {
                DoUpdateTrainTimes();
            }
            finally
            {
                mTmrCheck.Start();
            }
        }

        private void LoadCity()
        {
            using (var context = new mdfqyContext())
            {
                var cities = context.qy_city.ToList();

                foreach(var city in cities)
                {
                    mCities.Add(city.citycode, city);
                }
            }
        }

        private void LoadLine()
        {
            using (var context = new mdfqyContext())
            {
                var lines = context.qy_line.ToList();
                foreach(var line in lines)
                {
                    mLines.Add(line.id, line);
                }
            }
        }

        private void LoadStation()
        {
            using (var context = new mdfqyContext())
            {
                var stationes = context.qy_station.ToList();
                foreach(var station in stationes)
                {
                    mStations.Add(station.id, station);
                }
            }
        }

        private  void LoadTrainSchedule()
        {
            using (var context = new mdfqyContext())
            {
                var trainTimes = context.qy_train_schedule.ToList();
                foreach(var train in trainTimes)
                {
                    train_schedule_item trainItem = new train_schedule_item();
                    trainItem.trainSchedule = train;
                    trainItem.arriveTimeSecondsInDay = GetSecondsInDay(train.arrive_time, train.arrive_day);
                    trainItem.departTimeSecondsInDay = GetSecondsInDay(train.departure_time, train.arrive_day);
                    mTrainSchedules.Add(trainItem);
                }
            }
        }

        private void LoadPlatform()
        {
            using (var context = new mdfqyContext())
            {
                mPlatforms = context.qy_platform.ToList();
            }
        }

        private int GetSecondsInDay(string hhmm, int addDay)
        {
            int seconds = 0;
            hhmm = hhmm.Replace("：", ":");
            string[] array = hhmm.Split(':');
            seconds = int.Parse(array[0]) * 3600 + int.Parse(array[1]) * 60 + addDay * 86400;
            return seconds;
        }

        private int GetClosestIndexByLess(List<train_schedule_item> trains, int curSecondsInDay)
        {
            var result = trains.Where(x => x.arriveTimeSecondsInDay < curSecondsInDay).OrderByDescending(x => x.arriveTimeSecondsInDay);
            return result.Any() ? trains.IndexOf(result.FirstOrDefault()) : -1;
        }
    }

    sealed class train_schedule_item
    {
        public qy_train_schedule trainSchedule;
        public int arriveTimeSecondsInDay = 0;
        public int departTimeSecondsInDay = 0;
    }

    sealed class train_time_info
    {
        public int stationId = 0;
        public int origStationId = 0;
        public string origStationName = "";
        public int terminusId = 0;
        public string terminusName = "";
        public int currentTrainSeconds = 0;
        public int netTrainSeconds = 0;
    }
}
